package com.app.util.db.tool;

import java.sql.SQLException;
import java.util.Enumeration;

import com.app.util.db.bean.ResultList;
import com.app.util.db.bean.ResultMap;

/**
 * 查询遍历器，用来遍历大的数据集而无需一次性将数据加载到内存
 * 
 * @author ym
 */
@SuppressWarnings("unchecked")
public class SelectEnumeration implements Enumeration {

	private int pageIndex;
	private int pageSize;

	private long firstOffset;
	private int maxCount;
 
	private String sql;
	private Object[] param;

	private Select select;
	private ResultList list;
	private Class<?> clazz;

	private ResultMap tmp;

	private int pollCount;

	/**
	 * 构造一个查询遍历器
	 * 
	 * @param select
	 *            查询对象
	 * @param sql
	 *            查询语句
	 * @param param
	 *            查询参数
	 * @param pageSize
	 *            每次记录数量
	 * @param clazz
	 *            将记录转换成的类对象
	 */
	public SelectEnumeration(Select select, String sql, Object[] param, int pageSize, Class<?> clazz) {
		super();
		this.pageSize = pageSize;
		this.sql = sql;
		this.param = param;
		this.select = select;
		this.clazz = clazz;
	}

	/**
	 * 构造一个查询遍历器
	 * 
	 * @param select
	 *            查询对象
	 * @param sql
	 *            查询语句
	 * @param param
	 *            查询参数
	 * @param pageSize
	 *            每次记录数量
	 */
	public SelectEnumeration(Select select, String sql, Object[] param, int pageSize) {
		this(select, sql, param, pageSize, null);
	}

	/**
	 * 构造一个查询遍历器
	 * 
	 * @param select
	 *            查询对象
	 * @param sql
	 *            查询语句
	 * @param param
	 *            查询参数
	 */
	public SelectEnumeration(Select select, String sql, Object[] param) {
		this(select, sql, param, 1000);
	}

	/**
	 * 构造一个查询遍历器
	 * 
	 * @param select
	 *            查询对象
	 * @param sql
	 *            查询语句
	 */
	public SelectEnumeration(Select select, String sql) {
		this(select, sql, null, 1000);
	}

	/**
	 * 构造一个查询遍历器
	 * 
	 * @param select
	 *            查询对象
	 * @param sql
	 *            查询语句
	 * @param clazz
	 *            将记录转换成的类对象
	 */
	public SelectEnumeration(Select select, String sql, Class<?> clazz) {
		this(select, sql, null, 1000, clazz);
	}

	/**
	 * 构造一个查询遍历器
	 * 
	 * @param select
	 * @param sql
	 * @param params
	 * @param clazz
	 */
	public SelectEnumeration(Select select, String sql, Object[] params, Class<?> clazz) {
		this(select, sql, params, 1000, clazz);
	}

	private ResultMap poll() throws SQLException {
		if (null == list) {
			list = nextList();
			if (list.size() == 0) {
				return null;
			}
		}
		if (list.size() == 0) {
			list = null;
			return poll();
		}
		return list.poll();
	}

	/**
	 * 重置遍历状态，下次将从第1条数据开始返回数据记录。
	 */
	public void reset() {
		list = null;
		pageIndex = 0;
		pollCount = 0;
	}

	private ResultList nextList() throws SQLException {
		if (null == param || param.length == 0) {
			list = select.select(sql + " LIMIT " + (pageIndex * pageSize + firstOffset) + "," + pageSize);
		} else {
			list = select.select(sql + " LIMIT " + (pageIndex * pageSize + firstOffset) + "," + pageSize, param);
		}
		if (list.size() > 0) {
			pageIndex++;
		}
		return list;
	}

	/**
	 * 是否有下一条数据
	 */
	public boolean hasMoreElements() {
		try {
			if (maxCount > 0 && pollCount >= maxCount) {
				tmp = null;
				return false;
			}
			tmp = poll();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
		if (null == tmp) {
			return false;
		}
		return true;
	}

	/**
	 * 返回下一条数据
	 */
	public Object nextElement() {
		Object o = parseType();
		if (null != o) {
			pollCount++;
		}
		return o;
	}

	private Object parseType() {
		if (null == clazz) {
			return tmp;
		}
		if (clazz.equals(ResultMap.class)) {
			return tmp;
		} else {
			return select.parseObject(clazz, tmp);
		}
	}

	public String getSql() {
		return sql;
	}

	public void setSql(String sql) {
		this.sql = sql;
	}

	public long getPageIndex() {
		return pageIndex;
	}

	public int getPageSize() {
		return pageSize;
	}

	public Object[] getParam() {
		return param;
	}

	public Select getSelect() {
		return select;
	}

	public Class<?> getClazz() {
		return clazz;
	}

	public int getPollCount() {
		return pollCount;
	}

	public long getFirstOffset() {
		return firstOffset;
	}

	public long getNewOffset() {
		return pollCount + firstOffset;
	}

	public void setFirstOffset(long firstOffset) {
		this.firstOffset = firstOffset;
	}

	public int getMaxCount() {
		return maxCount;
	}

	public void setMaxCount(int maxCount) {
		this.maxCount = maxCount;
	}
}
